﻿using DropNet;
using DropNet.Exceptions;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace CloudStorageLight.Core
{
    public class DropBoxBlobAdapter: BlobAdapterBase
    {
        string appKey;
        string appSecret;
        string accessToken;
        string accessSecret;
        internal string dropboxFilePath;

        public DropBoxBlobAdapter(string rootPath, string accessToken, string accessSecret, string dropboxFilePath, string timezone = null)
            : this(rootPath, SystemSettings.Instance.DropBoxAppKey, SystemSettings.Instance.DropBoxAppSecret, accessToken, accessSecret, dropboxFilePath,timezone)
        {

        }
        public DropBoxBlobAdapter(string rootPath, string appKey, string appSecret, string accessToken, string accessSecret, string dropboxFilePath, string timezone)
        {
            this.RootPath = rootPath;
            this.appKey = appKey;
            this.appSecret = appSecret;
            this.accessToken = accessToken;
            this.accessSecret = accessSecret;
            this.dropboxFilePath = dropboxFilePath.TrimEnd('/')+"/";
            this.TimeZone = timezone;
        }

        public string ConvertDropPathToUrlPath(string dropPath)
        {
            if (dropboxFilePath.Length > dropPath.Length) return RootPath;
            var uriPath = dropPath.Substring(dropboxFilePath.Length);
            uriPath = RootPath + uriPath;
            return uriPath;
        }

        public string ConvertUrlPathToDropPath(string urlPath)
        {
            return this.dropboxFilePath + urlPath.Substring(RootPath.Length);
        }

        private DropNetClient GetDropBoxClient()
        {
            return new DropNetClient(this.appKey, this.appSecret, this.accessToken, this.accessSecret);
        }


        /// <summary>
        /// 指定したファイルが存在するか
        /// </summary>
        /// <param name="path"></param>
        /// <returns></returns>
        #pragma warning disable 1998
        protected override Task<bool> ExistsAsyncInternal(string path)
        {

            var client = GetDropBoxClient();
            var dropPath = ConvertUrlPathToDropPath(path);
            if (dropPath == "/") return Task.FromResult(true);
            var parentPath = GetParentFolder(dropPath);
            var result = client.GetMetaData(parentPath, null, true, false).Contents.Any(x => x.Is_Dir ? x.Path + "/" == dropPath : x.Path == dropPath);
            return Task.FromResult<bool>(result);

        }
        #pragma warning restore 1998

        string GetParentFolder(string path)
        {
            path = path.TrimEnd('/');
            return path.Substring(0, path.Length - path.Split('/').Last().Length);
        }


        /// <summary>
        /// Blobのコピー
        /// </summary>
        /// <param name="path"></param>
        /// <param name="fromUri"></param>
        /// <returns></returns>
        #pragma warning disable 1998
        public override async Task CopyAsync(string path, BlobFile file)
        {
            var client = GetDropBoxClient();
            var dropPathDest = ConvertUrlPathToDropPath(path);
            var dropPathSrc = ConvertUrlPathToDropPath(file.Path);
            client.Copy(dropPathSrc, dropPathDest);
        }
        #pragma warning restore 1998

        /// <summary>
        /// 削除
        /// </summary>
        /// <returns></returns>
        #pragma warning disable 1998
        public override async Task<bool> DeleteAsync(BlobItem file)
        {

            var client = GetDropBoxClient();
            var dropFilePath = ConvertUrlPathToDropPath(file.Path);

            var parentPath = GetParentFolder(dropFilePath);
            if (!client.GetMetaData(parentPath, null, true, false).Contents.Any(x => x.Is_Dir ? x.Path +"/" == dropFilePath : x.Path == dropFilePath))
            {
                return false;
            }

            var result = client.Delete(dropFilePath);
            ClearExistsCahce();
            return result.Is_Deleted;

        }
        #pragma warning restore 1998

        /// <summary>
        /// ディレクトリののファイル一覧を取得する
        /// </summary>
        /// <param name="id"></param>
        /// <param name="maxSize"></param>
        /// <returns></returns>
        #pragma warning disable 1998
        public override async Task<List<BlobItem>> GetFilesAsync(string id, int maxSize, bool flat = false)
        {
            var client = GetDropBoxClient();
            var result = new List<BlobItem>();
            var dropFilePath = ConvertUrlPathToDropPath(id.StartsWith(RootPath) ? id : RootPath);

            ListFiles(dropFilePath, maxSize, client, result, flat);
            return result;
        }
        #pragma warning restore 1998

        private void ListFiles(string id, int maxSize, DropNetClient client, List<BlobItem> result, bool flat)
        {
            var folders = new List<BlobDirectory>();
            foreach (var item in client.GetMetaData(id, null, true, false).Contents.OrderBy(x=>x.Path))
            {
                var logicalPath = ConvertDropPathToUrlPath(item.Path);
                BlobItem fileItem = null;
                if (item.Is_Dir)
                {
                    var folder = new BlobDirectory(Parent, logicalPath);
                    folders.Add(folder);
                    fileItem = folder;
                }
                else
                {
                    fileItem = new BlobFile(Parent, logicalPath);
                    fileItem.Length = item.Bytes;
                    var modified = DateTime.SpecifyKind(item.UTCDateModified, DateTimeKind.Utc);
                    fileItem.LastModified = ConvertTimezone(modified);
                }
                if (fileItem != null) result.Add(fileItem);
                if (maxSize > 0 && result.Count >= maxSize)
                {
                    break;
                }
            }

            if (flat)
            {
                foreach (var folder in folders)
                {
                    ListFiles(ConvertUrlPathToDropPath(folder.Path), maxSize, client, result, flat);
                    if (maxSize > 0 && result.Count >= maxSize)
                    {
                        break;
                    }
                }
            }
        }


        /// <summary>
        /// アップロード
        /// </summary>
        /// <param name="stream"></param>
        /// <returns></returns>
        #pragma warning disable 1998
        public override async Task UploadAsync(BlobItem item, Stream stream)
        {
            var client = GetDropBoxClient();
            var dropPath = ConvertUrlPathToDropPath(item.Path);
            var dropFolder = GetParentFolder(dropPath);

            client.UploadFile(dropFolder, item.Name, stream);
            ClearExistsCahce();
        }
        #pragma warning restore 1998

        /// <summary>
        /// アップロード
        /// </summary>
        /// <param name="localpath"></param>
        /// <returns></returns>
        public override async Task UploadAsync(BlobItem item, string localpath)
        {
            using (var st = new FileStream(localpath, FileMode.Open))
            {
                await UploadAsync(item, st);
            }
        }

        /// <summary>
        /// ダウンロード
        /// </summary>
        /// <param name="stream"></param>
        /// <returns></returns>
        public override async Task DownloadAsync(BlobItem item, Stream stream)
        {
            var client = GetDropBoxClient();
            var dropPath = ConvertUrlPathToDropPath(item.Path);

            var buff = client.GetFile(dropPath);
            await stream.WriteAsync(buff, 0, buff.Length);
        }

        /// <summary>
        /// ダウンロード
        /// </summary>
        /// <param name="localpath"></param>
        /// <returns></returns>
        public override async Task DownloadAsync(BlobItem item, string localpath)
        {
            using (var st = new FileStream(localpath, FileMode.Create))
            {
                await DownloadAsync(item, st);
            }
        }

        /// <summary>
        /// フォルダ作成
        /// </summary>
        /// <param name="dire"></param>
        /// <returns></returns>
        public override async Task CreateDirectoryAsync(BlobDirectory dire)
        {
            if (await ExistsAsync(dire.Path)) return;
            var client = GetDropBoxClient();
            var dropPath = ConvertUrlPathToDropPath(dire.Path);
            client.CreateFolder(dropPath);
            ClearExistsCahce();
        }

        /// <summary>
        /// メタ情報の設定
        /// </summary>
        /// <returns></returns>
        #pragma warning disable 1998
        public override async Task SetMetadataAsync(BlobItem item)
        {
            item.Link = ConvertUrlPathToDropPath(item.Path);

        }
        #pragma warning restore 1998

        /// <summary>
        /// メタ情報の取得
        /// </summary>
        /// <param name="fetch"></param>
        /// <returns></returns>
        #pragma warning disable 1998
        public override async Task GetMetadataAsync(BlobItem file)
        {
            var client = GetDropBoxClient();
            var dropPath = ConvertUrlPathToDropPath(file.Path);
            try
            {
                var meta = client.GetMetaData(dropPath, null, false, false);
                if (!meta.Is_Deleted)
                {
                    var modified = DateTime.SpecifyKind(meta.UTCDateModified, DateTimeKind.Utc);
                    file.LastModified = ConvertTimezone(modified);
                    file.Length = meta.Bytes;
                }
            }
            catch (DropboxRestException ex)
            {
                //IGNORE NOT FOUND
                Debug.WriteLine(ex.ToString());
            }

        }
        #pragma warning restore 1998

        /// <summary>
        /// 後処理
        /// </summary>
        public override void Dispose()
        {

        }
    }

}
